package rlib

import (
	"encoding/json"
	"errors"
	"flag"
	"io/ioutil"
	"net/http"

	"github.com/golang/glog"
)

// HTTP prefix

const (
	HTTPPrefix = "/raftserver"
)

//

// ERR
var (
	ErrLogIdTooLarge              = errors.New("log id too large")
	ErrNoHeartBeatInPastTimeRange = errors.New("no heartbeat in past time range")
	ErrAddNodeFailed              = errors.New("add new node failed")
)

// FLAGS
var (
	AutoStart = flag.Bool("autoStart", true, "if you don't want to call 'control/Begin' to start , flag it")
)

// req interface

type ApiReq interface {
	GetName() string
}

// rsp interface

type ApiRsp interface {
	GetName() string
	GetState() string
}

// -----------------------------------

type RaftServer struct {
	Rc *RaftCore

	BgCh chan int
}

func (rs *RaftServer) Init() {
	rs.Rc = &RaftCore{}
	rs.BgCh = make(chan int, 1)
	if *AutoStart {
		rs.BgCh <- 1
	}
	rs.Rc.Init(rs.BgCh)
}

type CallForVoteReq struct {
	Term     uint64
	LogId    uint64
	NodeName string
}

func (cfvreq *CallForVoteReq) GetName() string {
	return "CallForVote"
}

type CallForVoteRsp struct {
	ErrMsg string
}

func (cfvrsp *CallForVoteRsp) GetName() string {
	return "CallForVote"
}

func (cfvrsp *CallForVoteRsp) GetState() string {
	return cfvrsp.ErrMsg
}

// candidate 请求这个，获取选票
func (rs *RaftServer) CallForVote(w http.ResponseWriter, r *http.Request) {
	bodyByte, err := ioutil.ReadAll(r.Body)
	if err != nil {
		glog.Error(err)
		return
	}
	var req CallForVoteReq
	var rsp CallForVoteRsp
	json.Unmarshal(bodyByte, &req)
	rs.Rc.CallForVote(&req, &rsp)
	rspByte, err := json.Marshal(&rsp)
	if err != nil {
		glog.Error(err)
		return
	}
	w.Header().Set("Content-type", "application/json")
	_, err = w.Write(rspByte)
	if err != nil {
		glog.Error(err)
		return
	}
}

type PullLogReq struct {
	LogId uint64
}

func (plreq *PullLogReq) GetName() string {
	return "PullLog"
}

type PullLogRsp struct {
	LogItems []string
	ErrMsg   string
}

func (plrsp *PullLogRsp) GetName() string {
	return "PullLog"
}

func (plrsp *PullLogRsp) GetState() string {
	return plrsp.ErrMsg
}

func (rs *RaftServer) PullLog(w http.ResponseWriter, r *http.Request) {
	bodyByte, err := ioutil.ReadAll(r.Body)
	if err != nil {
		glog.Errorln(err)
	}
	var req PullLogReq
	var rsp PullLogRsp
	json.Unmarshal(bodyByte, &req)
	rs.Rc.PullLog(&req, &rsp)
	rspByte, err := json.Marshal(&rsp)
	if err != nil {
		glog.Error(err)
		return
	}
	w.Header().Set("Content-type", "application/json")
	_, err = w.Write(rspByte)
	if err != nil {
		glog.Error(err)
		return
	}
}

type BeatReq struct {
	MasterName string
}

func (breq *BeatReq) GetName() string {
	return "Beat"
}

type BeatRsp struct {
	ErrMsg string
}

func (brsp *BeatRsp) GetName() string {
	return "Beat"
}

func (brsp *BeatRsp) GetState() string {
	return brsp.ErrMsg
}

func (rs *RaftServer) Beat(w http.ResponseWriter, r *http.Request) {
	bodyByte, err := ioutil.ReadAll(r.Body)
	if err != nil {
		glog.Errorln(err)
	}
	var req BeatReq
	var rsp BeatRsp
	json.Unmarshal(bodyByte, &req)
	rs.Rc.Beat(&req, &rsp)
	rspByte, err := json.Marshal(&rsp)
	if err != nil {
		glog.Error(err)
		return
	}
	w.Header().Set("Content-type", "application/json")
	_, err = w.Write(rspByte)
	if err != nil {
		glog.Error(err)
		return
	}
}

type ControlBeginReq struct {
	SessionId string
}

func (cbreq *ControlBeginReq) GetName() string {
	return "ControlBegin"
}

type ControlBeginRsp struct {
	ErrMsg string
}

func (cbrsp *ControlBeginRsp) GetName() string {
	return "ControlBegin"
}

func (cbrsp *ControlBeginRsp) GetState() string {
	return cbrsp.ErrMsg
}

func (rs *RaftServer) ControlBegin(w http.ResponseWriter, r *http.Request) {
	bodyByte, err := ioutil.ReadAll(r.Body)
	if err != nil {
		glog.Errorln(err)
	}
	var req ControlBeginReq
	var rsp ControlBeginRsp
	json.Unmarshal(bodyByte, &req)
	rsp = ControlBeginRsp{ErrMsg: "OK"}
	rspByte, err := json.Marshal(&rsp)
	if err != nil {
		glog.Error(err)
		return
	}
	rs.BgCh <- 1
	w.Header().Set("Content-type", "application/json")
	_, err = w.Write(rspByte)
	if err != nil {
		glog.Error(err)
		return
	}
}

type RegisterReq struct {
	Addr string
	Name string
}

func (rreq *RegisterReq) GetName() string {
	return "Register"
}

type RegisterRsp struct {
	Conf   ClusterConfig
	ErrMsg string
}

func (rrsp *RegisterRsp) GetName() string {
	return "ControlBegin"
}

func (rrsp *RegisterRsp) GetState() string {
	return rrsp.ErrMsg
}

func (rs *RaftServer) Register(w http.ResponseWriter, r *http.Request) {
	bodyByte, err := ioutil.ReadAll(r.Body)
	if err != nil {
		glog.Errorln(err)
	}
	var req RegisterReq
	var rsp RegisterRsp
	json.Unmarshal(bodyByte, &req)
	rs.Rc.Register(&req, &rsp)
	rspByte, err := json.Marshal(&rsp)
	if err != nil {
		glog.Error(err)
		return
	}
	w.Header().Set("Content-type", "application/json")
	_, err = w.Write(rspByte)
	if err != nil {
		glog.Error(err)
		return
	}
}

func InitRaftServerHttpApi(rs *RaftServer) {
	http.HandleFunc(HTTPPrefix+"/CallForVote", rs.CallForVote)
	http.HandleFunc(HTTPPrefix+"/PullLog", rs.PullLog)
	http.HandleFunc(HTTPPrefix+"/Beat", rs.Beat)

	http.HandleFunc(HTTPPrefix+"/Register", rs.Register)

	// control
	http.HandleFunc(HTTPPrefix+"/control/Begin", rs.ControlBegin)
}
